<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <script>
        let [q,b,c] = [1,2,3];//从数组中提取值，按照对应位置，对变量赋值。
        //只要等号两边的模式相同，左边的变量就会被赋予对应的值
        let [foo, [[bar], baz]] = [1, [[2], 3]];
 //foo 1
 //bar 2
 //baz 3

let [ , , third] = ["foo", "bar", "baz"];
 //third "baz"

let [x, , y] = [1, 2, 3];
 //x 1
 //y 3
 let [head, ...tail] = [1, 2, 3, 4];
 //head 1
 //tail [2, 3, 4]
 let [x, y, ...z] = ['a'];
 //x "a"
 //y undefined 解构不成功，为defined
 //z []  对应为空

 let [foo] = 1;
let [foo] = false;
let [foo] = NaN;
let [foo] = undefined;
let [foo] = null;
let [foo] = {};
//都会报错，因为等号右边的值，要么转为对象以后不具备 Iterator 接口（前五个表达式），
//要么本身就不具备 Iterator 接口（最后一个表达式）


// Set 结构，也可以使用数组的解构赋值。

let [x, y, z] = new Set(['a', 'b', 'c']);
 //x "a"

 //只要某种数据结构具有 Iterator 接口，都可以采用数组形式的解构赋值。
function* fibs() {
  let a = 0;
  let b = 1;
  while (true) {
    yield a;
    [a, b] = [b, a + b];//b的值赋给a，将a+b的值赋给b
  }
}
let [first, second, third, fourth, fifth, sixth] = fibs();
sixth // 5

//解构赋值允许指定默认值。

let [foo = true] = [];
//foo  true
let [x, y = 'b'] = ['a']; // x='a', y='b'
let [x, y = 'b'] = ['a', undefined]; // x='a', y='b'

let [x = 1] = [undefined];
 //x 1
//必须严格等于undefined，默认值才会生效
let [x = 1] = [null];
 //x null
//如果一个数组成员是null，默认值就不会生效，因为null不严格等于undefined

//默认值可以引用解构赋值的其他变量，但该变量必须已经声明。

let [x = 1, y = x] = [];     // x=1; y=1
let [x = 1, y = x] = [2];    // x=2; y=2
let [x = 1, y = x] = [1, 2]; // x=1; y=2
let [x = y, y = 1] = [];     // ReferenceError: y is not defined
//默认值会被覆盖，后面赋值将其覆盖

//2
//解构不仅可以用于数组，还可以用于对象。

let { foo, bar } = { foo: 'aaa', bar: 'bbb' };
foo // "aaa"
bar // "bbb"
let { log, sin, cos } = Math;

// 例二
const { log } = console;//将Math对象的对数、正弦、余弦三个方法，赋值到对应的变量上，
log('hello') // hello       //console.log赋值到log变量。

let { foo: baz } = { foo: 'aaa', bar: 'bbb' };
baz // "aaa" 对象的解构赋值的内部机制，是先找到同名属性，然后再赋给对应的变量，真正被赋值的是变量

let obj = { first: 'hello', last: 'world' };
let { first: f, last: l } = obj;
f // 'hello'
l // 'world'

let obj = {
  p: [
    'Hello',
    { y: 'World' }
  ]
};

let { p: [x, { y }] } = obj;
x // "Hello"
y // "World"

//这时p是模式，不是变量，因此不会被赋值。如果p也要作为变量赋值，可以写成下面这样。

let obj = {
  p: [
    'Hello',
    { y: 'World' }
  ]
};

let { p, p: [x, { y }] } = obj;
x // "Hello"
y // "World"
p // ["Hello", {y: "World"}]

let {foo: {bar}} = {baz: 'baz'};
//等号左边对象的foo属性，对应一个子对象。该子对象的bar属性，解构时会报错。因为foo这时等于undefined，再取子属性就会报错


const obj1 = {};
const obj2 = { foo: 'bar' };
Object.setPrototypeOf(obj1, obj2);

const { foo } = obj1;
 //foo "bar"

//对象obj1的原型对象是obj2。foo属性不是obj1自身的属性，而是继承自obj2的属性，解构赋值可以取到这个属性。
   
// 错误的写法
let x;
//{x} = {x: 1};
// SyntaxError: syntax error

//上面代码写法会报错，因为 JavaScript 引擎会将{x}理解成一个代码块，从而发生语法错误。
//只有不将大括号写在行首，避免 JavaScript 将其解释为代码块，才能解决这个问题。

// 正确的写法
let x;
({x} = {x: 1});

let arr = [1, 2, 3];
let {0 : first, [arr.length - 1] : last} = arr;
first // 1
last // 3
   
//3
const [a, b, c, d, e] = 'hello';
a // "h"
b // "e"
c // "l"
d // "l"
e // "o"

//类似数组的对象都有一个length属性

let {length : len} = 'hello';
len // 5
//解构赋值时，如果等号右边是数值和布尔值，则会先转为对象。

let {toString: s} = 123;
s === Number.prototype.toString // true

let {toString: s} = true;
s === Boolean.prototype.toString // true

//只要等号右边的值不是对象或数组，就先将其转为对象。由于undefined和null无法转为对象，所以对它们进行解构赋值，都会报错。

let { prop: x } = undefined; // TypeError
let { prop: y } = null; // TypeError

function add([x, y]){
  return x + y;
}

[[1, 2], [3, 4]].map(([a, b]) => a + b);
// [ 3, 7 ]

//函数参数的解构也可以使用默认值。

function move({x = 0, y = 0} = {}) {//move({x, y} = { x: 0, y: 0 }
//为move函数设定默认值，后面为x，y设定默认值
  return [x, y];
}

move({x: 3, y: 8}); // [3, 8]
move({x: 3}); // [3, 0]
move({}); // [0, 0]
move(); // [0, 0]
add([1, 2]); // 3

// 全部报错
// let [(a)] = [1];

// let {x: (c)} = {};
// let ({x: c}) = {};
// let {(x: c)} = {};
// let {(x): c} = {};

// let { o: ({ p: p }) } = { o: { p: 2 } };

//可以使用圆括号的情况只有一种：赋值语句的非模式部分，可以使用圆括号。

[(b)] = [3]; // 正确
({ p: (d) } = {}); // 正确
[(parseInt.prop)] = [3]; // 正确

//交换变量的值

let x = 1;
let y = 2;

[x, y] = [y, x];
//函数只能返回一个值，如果要返回多个值，只能将它们放在数组或对象里返回。


// 返回一个数组

function example() {
  return [1, 2, 3];
}
let [a, b, c] = example();

// 返回一个对象

function example() {
  return {
    foo: 1,
    bar: 2
  };
}
let { foo, bar } = example();

//Map 结构原生支持 Iterator 接口，配合变量的解构赋值，获取键名和键值就非常方便。

const map = new Map();
map.set('first', 'hello');
map.set('second', 'world');

for (let [key, value] of map) {
  console.log(key + " is " + value);
}
// first is hello
// second is world

   </script>
</body>
</html>